package io.r2mo.spring.common.webflow;

import cn.hutool.core.io.IoUtil;
import io.r2mo.function.Fn;
import io.r2mo.spring.common.webflow.attachment.MultipartProcessor;
import io.r2mo.typed.common.Pagination;
import io.r2mo.typed.json.JObject;
import io.r2mo.typed.service.ActResponse;
import io.r2mo.typed.service.ActState;
import io.r2mo.typed.webflow.R;
import io.r2mo.typed.webflow.WebRequest;
import io.r2mo.typed.webflow.WebResponse;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

/**
 * Crud 专用 Controller
 *
 * @author lang : 2025-09-04
 */
@Slf4j
public abstract class BaseController<
    T, REQ extends WebRequest<T>, RESP extends WebResponse<T>
    > extends SuperController<T> {

    public R<RESP> createSingle(final REQ request) {
        Objects.requireNonNull(request, "[ R2MO ] 请求对象不能为空！");
        // 请求转换
        final T entity = request.data();
        // 保存
        final ActResponse<T> executed = this.service().create(entity);
        /* 201 / 200 */
        return this.replySuccess(executed, this::createResponse);
    }

    public R<RESP> updateSingle(final String id, final REQ request) {
        Objects.requireNonNull(request, "[ R2MO ] 请求对象不能为空！");
        request.id(id); // 双重检查
        // 请求转换
        final T entity = request.data();
        // 更新
        final ActResponse<T> executed = this.service().updateById(id, entity);
        /* 204 / 200 */
        return this.replySuccess(executed, this::createResponse);
    }

    public R<T> findSingle(final String id) {
        final ActResponse<T> executed = this.service().findById(id);
        /* 204 / 200 */
        return this.replySuccess(executed);
    }

    public R<Boolean> removeSingle(final String id) {
        final ActResponse<Boolean> executed = this.service().removeById(id);
        /* 204 / 200 */
        return this.replySuccessOr(executed);
    }

    public R<Pagination<T>> findPage(final JObject query) {
        Objects.requireNonNull(query, "[ R2MO ] 请求对象不能为空！");
        // 查询
        final ActResponse<Pagination<T>> executed = this.service().findPage(query);
        // 响应
        return R.ok(executed.data());
    }

    public R<List<T>> findAll() {
        final REQ request = this.createRequest();
        final ActResponse<List<T>> executed = this.service().findAll(request.getAppId(), request.getTenantId());
        // 响应
        return R.ok(executed.data());
    }

    public R<Boolean> uploadData(final MultipartFile file, final JObject config) {
        // 数据转换
        final MultipartProcessor<T> serviceAttachment = this.multipartProcessor();
        final List<T> imported = serviceAttachment.toMany(file,
            this.multipartMeta(config, true));
        // 批量保存
        final ActResponse<List<T>> executed = this.service().saveBatch(imported);
        return R.ok(ActState.SUCCESS == executed.state());
    }

    @SuppressWarnings("all")
    public void downloadBy(final JObject criteria) {
        Objects.requireNonNull(criteria, "[ R2MO ] 请求对象不能为空！");
        // 查询数据
        final ActResponse<List<T>> executed = this.service().findMany(criteria);
        if (ActState.SUCCESS_204_NO_DATA == executed.state()) {
            log.warn("[ R2MO ] 无数据可供导出！");
            return;
        }

        // 导出
        final HttpServletResponse response = this.createResponse().response();
        if (Objects.isNull(response)) {
            log.error("[ R2MO ] 当前响应对象不可用，无法导出数据！");
            return;
        }


        response.setContentType("application/octet-stream; charset=UTF-8");

        final JObject config = criteria.getJObject("config");
        final MultipartProcessor<T> serviceAttachment = this.multipartProcessor();
        final InputStream binary = serviceAttachment.toBinary(executed.data(),
            this.multipartMeta(config, false));

        // 特殊参数
        final String filename = config.getString("filename", UUID.randomUUID().toString());
        response.setHeader("Content-Disposition", "attachment;filename=" + filename);
        Fn.jvmAt(() -> IoUtil.copy(binary, response.getOutputStream()));
    }

    protected abstract RESP createResponse();

    protected abstract REQ createRequest();
}
